package com.ssm.user.init;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class ApplicationInit implements ApplicationListener<ApplicationReadyEvent> {

    /**
     *注意：效果类似于前面Redis中实现的自动预热缓存，此处我们不进行预热数据，而是将线程池等资源进行预热！
     *
     * 情况说明：无论是线程池还前面的HttpClient，我们可能在生产中发现，我们的服务如果一开始就接收到了大流量，
     * 那么我们服务的承载能力是慢慢的起来的，而不是上来就满状态运行，这在大流量的情况下可能刚启动的时候会导致服务负载不够或者CPU瞬间飙高的情况！
     *
     * 思考：如果我们可以提前进行服务预热，例如将线程池核心线程创建好（而不是流量来了再慢慢创建），那么我们就可以避免相关的问题产生！
     */

    //我们使用函数式编程方式，来优雅地实现将预热的情况放入Map<方法描述，方法>中，最终在运行时取出并执行，顺便抛出出错的代码！

    Map<String, InitFunction> initFunctionMap = new HashMap<>();

    // 静态代码块随着类的加载而加载，非静态代码块随着对象的加载而加载
    // 所以静态代码块再调用类的时候执行。非静态代码块创建对象时或者通过反射获取其类信息的时候执行

    {
        initFunctionMap.put("预热fastjson", () -> this.initFastJson()); //this::方法名 可以令类中方法实现接口中的抽象方法
        initFunctionMap.put("预热线程池", this::initThreadPool);
    }



    /**
     * 服务启动完成后，所有想要执行的操作，都可以在此执行
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        initFunctionMap.forEach((desc, function) -> {
            try {
                long start = System.currentTimeMillis();
                function.invoke();
                log.info("ApplicationInit{}.costTime{}", desc, System.currentTimeMillis() - start);
            } catch (Exception e) {
                log.error("ApplicationInit{}.error", desc, e);
            }
        });
    }

    private void initFastJson() {
        System.out.println("预热fastjson");
    }

    private void initThreadPool() {
        System.out.println("预热线程池");
    }

    interface InitFunction {
        void invoke();
    }

}
